//****************************************************************
// File Name  : AR8035_mii_phy.v
// Author     : xianyong.chen
// Date       : 
// Description: 
//   It access MII registers 
//****************************************************************
`timescale 1ns/1ns

module AR8035_mii_phy(
        input   wire            sclk,
        input   wire            clk_enable,
        input   wire            rst_n,

        input   wire    [4:0]   phy_addr,
        input   wire    [4:0]   reg_addr,
        input   wire            wr_en,
        input   wire    [15:0]  wr_data,
        output  reg             wr_end,
        input   wire            rd_en,
        output  reg             rd_data_vld,
        output  reg     [15:0]  rd_data,

        output  reg             mdc,
        inout   tri             mdio
        );
//****************************************************************
reg     [3:0]   state;

localparam      IDLE            = 'd0;
localparam      PRE             = 'd1;
localparam      ST              = 'd2;
localparam      OP              = 'd3;
localparam      PHYAD           = 'd4;
localparam      REGAD           = 'd5;
localparam      TA              = 'd6;
localparam      DATA            = 'd7;
localparam      GAP             = 'd8;

localparam      MAX_PRE_CNT     = 32;
localparam      MAX_ST_CNT      = 2;
localparam      MAX_OP_CNT      = 2;
localparam      MAX_PHYAD_CNT   = 5;
localparam      MAX_REGAD_CNT   = 5;
localparam      MAX_TA_CNT      = 2;
localparam      MAX_DATA_CNT    = 16; 
localparam      MAX_GAP_CNT     = 4;

reg     [4:0]   pre_cnt;
reg             st_cnt;
reg             op_cnt;
reg     [2:0]   phyad_cnt;
reg     [2:0]   regad_cnt;
reg             ta_cnt;
reg     [3:0]   data_cnt;
reg     [1:0]   gap_cnt;

reg             mdc_out_en;
reg             mdio_out_en;
reg             mdio_out;
wire            mdio_in;
//****************************************************************
//state[3:0]
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        state <= IDLE;
    else if (clk_enable)
        case(state)
            IDLE:   if(wr_en==1 || rd_en==1)
                            state <= PRE;

            PRE:    if(pre_cnt==MAX_PRE_CNT-1)
                            state <= ST;

            ST:     if(st_cnt==MAX_ST_CNT-1)
                            state <= OP;

            OP:     if(op_cnt==MAX_OP_CNT-1)
                            state <= PHYAD;

            PHYAD:  if(phyad_cnt==MAX_PHYAD_CNT-1)
                            state <= REGAD;

            REGAD:  if(regad_cnt==MAX_REGAD_CNT-1)
                            state <= TA;

            TA:     if(ta_cnt==MAX_TA_CNT-1)
                            state <= DATA;

            DATA:   if(data_cnt==MAX_DATA_CNT-1)
                            state <= GAP;

            GAP:    if(gap_cnt==MAX_GAP_CNT-1)
                            state <= IDLE;

            default:        state <= IDLE;
        endcase
//****************************************************************
//pre_cnt[4:0]
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        pre_cnt <= 'b0;
    else if (clk_enable)
        begin
        if(state==PRE)
            pre_cnt <= pre_cnt + 1'b1;
        else
            pre_cnt <= 'b0;
        end

//st_cnt
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        st_cnt <= 'b0;
    else if (clk_enable)
        begin
        if(state==ST)
            st_cnt <= st_cnt + 1'b1;
        else
            st_cnt <= 'b0;
        end

//op_cnt
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        op_cnt <= 'b0;
    else if (clk_enable)
        begin
        if(state==OP)
            op_cnt <= op_cnt + 1'b1;
        else
            op_cnt <= 'b0;
        end

//phyad_cnt[2:0]
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        phyad_cnt <= 'b0;
    else if (clk_enable)
        begin
        if(state==PHYAD)
            phyad_cnt <= phyad_cnt + 1'b1;
        else
            phyad_cnt <= 'b0;
        end

//regad_cnt[2:0]
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        regad_cnt <= 'b0;
    else if (clk_enable)
        begin
        if(state==REGAD)
            regad_cnt <= regad_cnt + 1'b1;
        else
            regad_cnt <= 'b0;
        end

//ta_cnt
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        ta_cnt <= 'b0;
    else if (clk_enable)
        begin
        if(state==TA)
            ta_cnt <= ta_cnt + 1'b1;
        else
            ta_cnt <= 'b0;
        end

//data_cnt[3:0]
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        data_cnt <= 'b0;
    else if (clk_enable)
        begin
        if(state==DATA)
            data_cnt <= data_cnt + 1'b1;
        else
            data_cnt <= 'b0;
        end

//gap_cnt[1:0]
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        gap_cnt <= 'b0;
    else if (clk_enable)
        begin
        if(state==GAP)
            gap_cnt <= gap_cnt + 1'b1;
        else
            gap_cnt <= 'b0;
        end
//****************************************************************
//mdc_out_en
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        mdc_out_en <= 'b0;
    else if (clk_enable)
        begin
        if(state==IDLE || state==GAP)
            mdc_out_en <= 1'b0;
        else
            mdc_out_en <= 1'b1;
        end

//mdc
//assign mdc = (mdc_out_en==1) ? (~sclk) : 1'b0;
always @(posedge sclk or negedge rst_n)
    if (!rst_n)
        mdc <= 1'b0;
    else if (mdc_out_en)
        mdc <= ~mdc;
    else
        mdc <= 1'b0;

//mdio_out_en
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        mdio_out_en <= 'b0;
    else if (clk_enable)
        begin
        if(state==PRE || state==ST || state==OP || state==PHYAD || state==REGAD
            || (state==TA && wr_en==1) || (state==DATA && wr_en==1))
            mdio_out_en <= 1'b1;
        else
            mdio_out_en <= 'b0;
        end

//mdio_out
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        mdio_out <= 'b0;
    else if (clk_enable)
        case(state)
            PRE:    mdio_out <= 1'b1;

            ST:     case(st_cnt)
                            0:      mdio_out <= 1'b0;
                            1:      mdio_out <= 1'b1;
                    endcase

            OP:     case(op_cnt)
                            0:      mdio_out <= (wr_en==1) ? 1'b0 : 1'b1;
                            1:      mdio_out <= (wr_en==1) ? 1'b1 : 1'b0;
                    endcase

            PHYAD:  case(phyad_cnt)
                            0:      mdio_out <= phy_addr[4];
                            1:      mdio_out <= phy_addr[3];
                            2:      mdio_out <= phy_addr[2];
                            3:      mdio_out <= phy_addr[1];
                            4:      mdio_out <= phy_addr[0];
                    endcase

            REGAD:  case(regad_cnt)
                            0:      mdio_out <= reg_addr[4];
                            1:      mdio_out <= reg_addr[3];
                            2:      mdio_out <= reg_addr[2];
                            3:      mdio_out <= reg_addr[1];
                            4:      mdio_out <= reg_addr[0];
                    endcase

            TA:     if(wr_en==1)
                    case(ta_cnt)
                            0:      mdio_out <= 1'b1;
                            1:      mdio_out <= 1'b0;
                    endcase

            DATA:   if(wr_en==1)
                    case(data_cnt)
                            0:      mdio_out <= wr_data[15];
                            1:      mdio_out <= wr_data[14];
                            2:      mdio_out <= wr_data[13];
                            3:      mdio_out <= wr_data[12];
                            4:      mdio_out <= wr_data[11];
                            5:      mdio_out <= wr_data[10];
                            6:      mdio_out <= wr_data[9];
                            7:      mdio_out <= wr_data[8];
                            8:      mdio_out <= wr_data[7];
                            9:      mdio_out <= wr_data[6];
                            10:     mdio_out <= wr_data[5];
                            11:     mdio_out <= wr_data[4];
                            12:     mdio_out <= wr_data[3];
                            13:     mdio_out <= wr_data[2];
                            14:     mdio_out <= wr_data[1];
                            15:     mdio_out <= wr_data[0];
                    endcase
        endcase

//mdio
assign mdio = (mdio_out_en==1) ? mdio_out : 1'bz;

//wr_end
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        wr_end <= 'b0;
    else if (clk_enable)
        begin
        if(state==DATA && wr_en==1 && data_cnt==MAX_DATA_CNT-3)
            wr_end <= 1'b1;
        else
            wr_end <= 'b0;
        end
//****************************************************************
//mdio_in
assign mdio_in = mdio;

//rd_data[15:0]
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        rd_data <= 'b0;
    else if (clk_enable)
        begin
        if(state==DATA && rd_en==1)
            rd_data <= {rd_data[14:0],mdio_in};
        end

//rd_data_vld
always@(posedge sclk or negedge rst_n)
    if(!rst_n)
        rd_data_vld <= 'b0;
    else if (clk_enable)
        begin
        if(state==DATA && rd_en==1 && data_cnt==MAX_DATA_CNT-1)
            rd_data_vld <= 1'b1;
        else
            rd_data_vld <= 'b0;
        end
//****************************************************************
endmodule
